<template>
  <div class="wallet-wrap">
    <div class="btn-wrap">
      <div v-if="wallet.connected" class="coin">
        <span v-if="wallet.loading"><Icon type="loading" /></span>
        <span v-else class="text">{{ balance }} SOL</span>
      </div>
      <Button v-if="!wallet.connected" ghost @click="$accessor.wallet.openModal"> Connect to wallet </Button>
      <Button v-else ghost @click="$accessor.wallet.openModal">
        {{ wallet.address.substr(0, 4) }}
        ...
        {{ wallet.address.substr(wallet.address.length - 4, 4) }}
      </Button>
      <Popover placement="bottomRight" trigger="click">
        <template slot="title">
          <div class="pove-title">Transaction Settings</div>
          <div>
            <div class="pove-sub-title">Slippage tolerance</div>
            <div class="pove-span">
              <span>0.1%</span>
              <span>0.5%</span>
              <span>1%</span>
            </div>
          </div>
          <div>
            <div class="pove-sub-title">Transaction deadline</div>
            <div><input value="20" /><span>minutes</span></div>
          </div>
          <div>
            <div class="pove-title">Interface Settings</div>
            <div class="pove-switch">
              <span class="pove-sub-title">Toggle Expert Mode</span>
              <ASwitch checked-children="on" un-checked-children="off" default-checked></ASwitch>
            </div>
          </div>
        </template>
        <Button class="setting">...</Button>
      </Popover>
    </div>

    <Modal
      :title="!wallet.connected ? 'Connect to a wallet' : 'Your wallet'"
      :visible="wallet.modalShow"
      :footer="null"
      centered
      @cancel="$accessor.wallet.closeModal"
    >
      <div v-if="!wallet.connected" class="select-wallet">
        <Button v-for="(providerUrl, name) in wallets" :key="name" ghost @click="connect(name)">
          <span>{{ name }}</span>
          <img :src="importIcon(`/wallets/${name.replace(' ', '-').toLowerCase()}.png`)" />
        </Button>
      </div>
      <div v-else class="wallet-info">
        <p class="address">{{ wallet.address }}</p>

        <Button ghost class="dis-btn" @click="disconnect"> DISCONNECT </Button>
      </div>
    </Modal>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Watch } from 'nuxt-property-decorator'
import { Button, Modal, Icon, Switch, Popover } from 'ant-design-vue'
import { AccountInfo, Context } from '@solana/web3.js'
// @ts-ignore
import SolanaWalletAdapter from '@project-serum/sol-wallet-adapter'
import { mapState } from 'vuex'

import importIcon from '@/utils/import-icon'
import logger from '@/utils/logger'
import { web3Config, commitment } from '@/utils/web3'
import { NATIVE_SOL, TokenInfo } from '@/utils/tokens'
import { TokenAmount } from '@/utils/safe-math'
import {
  WalletAdapter,
  SolongWalletAdapter,
  MathWalletAdapter,
  PhantomWalletAdapter,
  LedgerWalletAdapter
} from '@/wallets'
import BigNumber from '@/../node_modules/bignumber.js/bignumber'

// fix: Failed to resolve directive: ant-portal
Vue.use(Modal)

interface Wallets {
  [key: string]: string
}

@Component({
  computed: {
    ...mapState(['wallet'])
  },

  components: {
    Button,
    Modal,
    Icon,
    Popover,
    ASwitch: Switch
  }
})
export default class Wallet extends Vue {
  /* ========== DATA ========== */
  wallets = {
    // Ledger: '',
    // 'Sollet Extension': '',
    Solong: '',
    // TrustWallet: '',
    // MathWallet: '',
    // Phantom: '',
    Sollet: 'https://www.sollet.io'
    // Solflare: 'https://solflare.com/access-wallet',
    // Bonfida: 'https://bonfida.com/wallet'
    // https://docs.coin98.app/coin98-extension/developer-guide
    // Coin98: ''
    // ezDeFi: '',
  } as Wallets

  balance = {} as BigNumber

  // auto refresh
  walletTimer: number | undefined = undefined
  priceTimer: number | undefined = undefined
  liquidityTimer: number | undefined = undefined
  farmTimer: number | undefined = undefined
  idoTimer: number | undefined = undefined
  // web3 listener
  walletListenerId = null as number | null

  /* ========== COMPUTED ========== */
  get wallet() {
    return this.$accessor.wallet
  }

  get price() {
    return this.$accessor.price
  }

  get liquidity() {
    return this.$accessor.liquidity
  }

  get farm() {
    return this.$accessor.farm
  }

  get ido() {
    return this.$accessor.ido
  }

  @Watch('wallet.tokenAccounts', { deep: true })
  watchBalance() {
    const solAccount = this.wallet.tokenAccounts
    // @ts-ignore
    const balance = solAccount[NATIVE_SOL.mintAddress].balance as TokenAmount

    this.balance = balance.toEther()
  }

  /* ========== LIFECYCLE ========== */
  async beforeMount() {
    await this.$accessor.price.requestPrices()
    await this.$accessor.swap.getMarkets()
    await this.$accessor.liquidity.requestInfos()
    // await this.$accessor.farm.requestInfos()

    this.setWalletTimer()
    this.setPriceTimer()
    this.setLiquidityTimer()
    // this.setFarmTimer()
    this.setIdoTimer()
  }

  beforeDestroy() {
    window.clearInterval(this.walletTimer)
    window.clearInterval(this.priceTimer)
    window.clearInterval(this.liquidityTimer)
    window.clearInterval(this.farmTimer)
    window.clearInterval(this.idoTimer)
  }

  /* ========== WATCH ========== */

  /* ========== METHODS ========== */
  importIcon = importIcon

  connect(walletName: string) {
    let wallet: WalletAdapter
    const { rpcs } = web3Config
    const endpoint = rpcs[0]

    switch (walletName) {
      case 'Ledger': {
        wallet = new LedgerWalletAdapter()
        break
      }
      case 'Sollet Extension': {
        if ((window as any).sollet === undefined) {
          this.$notify.error({
            message: 'Connect wallet failed',
            description: 'Please install and initialize Sollet wallet extension first'
          })
          return
        }

        wallet = new SolanaWalletAdapter((window as any).sollet, endpoint)
        break
      }
      case 'Solong': {
        if ((window as any).solong === undefined) {
          this.$notify.error({
            message: 'Connect wallet failed',
            description: 'Please install and initialize Solong wallet extension first'
          })
          return
        }

        wallet = new SolongWalletAdapter()
        break
      }
      case 'MathWallet': {
        if ((window as any).solana === undefined || !(window as any).solana.isMathWallet) {
          this.$notify.error({
            message: 'Connect wallet failed',
            description: 'Please install and initialize Math wallet extension first'
          })
          return
        }

        wallet = new MathWalletAdapter()
        break
      }
      case 'Phantom': {
        if ((window as any).solana === undefined || !(window as any).solana.isPhantom) {
          this.$notify.error({
            message: 'Connect wallet failed',
            description: 'Please install and initialize Phantom wallet extension first'
          })
          return
        }

        wallet = new PhantomWalletAdapter()
        break
      }
      default: {
        wallet = new SolanaWalletAdapter(this.wallets[walletName], endpoint)
        break
      }
    }

    wallet.on('connect', () => {
      this.$accessor.wallet.closeModal().then(() => {
        if (wallet.publicKey) {
          Vue.prototype.$wallet = wallet
          this.$accessor.wallet.setConnected(wallet.publicKey.toBase58())

          this.subWallet()
          this.$emit('connected', wallet.publicKey.toBase58())
          this.$notify.success({
            message: 'Wallet connected',
            description: ''
          })
        }
      })
    })

    wallet.on('disconnect', () => {
      this.disconnect()
    })

    try {
      wallet.connect()
    } catch (error) {
      this.$notify.error({
        message: 'Connect wallet failed',
        description: error.message
      })
    }
  }

  disconnect() {
    Vue.prototype.$wallet.disconnect()
    Vue.prototype.$wallet = null

    this.unsubWallet()

    this.$accessor.wallet.setDisconnected()
    this.$notify.warning({
      message: 'Wallet disconnected',
      description: ''
    })
  }

  onWalletChange(_accountInfo: AccountInfo<Buffer>, context: Context): void {
    logger('onAccountChange')

    const { slot } = context

    if (slot !== this.wallet.lastSubBlock) {
      this.$accessor.wallet.setLastSubBlock(slot)
      this.$accessor.wallet.getTokenAccounts()
      this.$accessor.farm.getStakeAccounts()
      this.$accessor.ido.getIdoAccounts()
    }
  }

  subWallet() {
    const wallet = this.$wallet
    if (wallet && wallet.publicKey) {
      this.walletListenerId = this.$web3.onAccountChange(wallet.publicKey, this.onWalletChange, commitment)

      this.$accessor.wallet.getTokenAccounts()
      // this.$accessor.farm.getStakeAccounts()
      this.$accessor.ido.getIdoAccounts()
    }
  }

  unsubWallet() {
    if (this.walletListenerId) {
      this.$web3.removeAccountChangeListener(this.walletListenerId)
    }
  }

  setWalletTimer() {
    this.walletTimer = window.setInterval(async () => {
      if (this.wallet.connected && !this.wallet.loading) {
        // vuex is connected but $wallet not, meaning window closed
        if (this.$wallet && this.$wallet.connected) {
          if (this.wallet.countdown < this.wallet.autoRefreshTime) {
            this.$accessor.wallet.setCountdown(this.$accessor.wallet.countdown + 1)
            if (this.wallet.countdown === this.wallet.autoRefreshTime) {
              await this.$accessor.wallet.getTokenAccounts()
            }
          }
        } else {
          this.disconnect()
        }
      }
    }, 1000)
  }

  setPriceTimer() {
    this.priceTimer = window.setInterval(async () => {
      if (!this.price.loading) {
        if (this.price.countdown < this.price.autoRefreshTime) {
          this.$accessor.price.setCountdown(this.$accessor.price.countdown + 1)
          if (this.price.countdown === this.price.autoRefreshTime) {
            await this.$accessor.price.requestPrices()
          }
        }
      }
    }, 1000)
  }

  setLiquidityTimer() {
    this.liquidityTimer = window.setInterval(async () => {
      if (!this.liquidity.loading) {
        if (this.liquidity.countdown < this.liquidity.autoRefreshTime) {
          this.$accessor.liquidity.setCountdown(this.$accessor.liquidity.countdown + 1)
          if (this.liquidity.countdown === this.liquidity.autoRefreshTime) {
            await this.$accessor.liquidity.requestInfos()
          }
        }
      }
    }, 1000)
  }

  setFarmTimer() {
    this.farmTimer = window.setInterval(async () => {
      if (!this.farm.loading) {
        if (this.farm.countdown < this.farm.autoRefreshTime) {
          this.$accessor.farm.setCountdown(this.$accessor.farm.countdown + 1)
          if (this.farm.countdown === this.farm.autoRefreshTime) {
            await this.$accessor.farm.requestInfos()
          }
        }
      }
    }, 1000)
  }

  setIdoTimer() {
    this.idoTimer = window.setInterval(async () => {
      if (!this.ido.loading) {
        if (this.ido.countdown < this.ido.autoRefreshTime) {
          this.$accessor.ido.setCountdown(this.$accessor.ido.countdown + 1)
          if (this.ido.countdown === this.ido.autoRefreshTime) {
            await this.$accessor.ido.requestInfos()
          }
        }
      }
    }, 1000)
  }
}
</script>

<style lang="less">
@import '../styles/variables';

.ant-modal-content {
  border-radius: 24px;
  background-color: #fff;
  .ant-modal-header {
    border-radius: 24px 24px 0 0;
    background-color: #fff;
    padding: 24px 24px 0 24px;
    border-bottom: none;
    > div {
      font-size: 24px;
      font-weight: bold;
    }
  }
  .ant-modal-close {
    color: @text-color;
  }
}

.select-wallet {
  button {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 30px 30px;
    width: 100%;
    height: 58px;
    text-align: left;
    border: 1px solid @s-color;
    border-radius: 18px;
    color: @p-color;
    > span {
      color: @p-color;
      font-size: 20px;
      font-weight: bold;
    }
    img {
      height: 32px;
      width: 32px;
      border-radius: 50%;
    }
  }

  button:not(:first-child) {
    margin-top: 10px;
  }
}
</style>

<style lang="less" scoped>
@import '../styles/variables';
.wallet-wrap {
  .btn-wrap {
    display: flex;
    align-items: center;
    min-width: 325px;
    justify-content: flex-end;
    .coin {
      border-radius: 12px;
      font-weight: bold;
      text-align: center;
      font-size: 18px;
      height: 48px;
      line-height: 48px;
      color: #ffa348;
      border: 1px solid #ffa348;
      margin-right: 12px;
      padding: 0 15px;
      .text {
        white-space: nowrap;
      }
    }
    .setting {
      margin-left: 12px;
      color: @gray-color;
      border: 1px solid @gray-color;
    }
  }
  button {
    border: 1px solid @s-color;
    font-size: 18px;
    height: 48px;
    border-radius: 12px;
    color: @s-color;
    > span {
      color: @s-color;
    }
  }
  .wallet-info {
    text-align: center;

    .address {
      font-size: 17px;
    }
  }
  .dis-btn {
    border: 1px solid @s-color;
    font-size: 18px;
    height: 48px;
    border-radius: 12px;
    color: @s-color;
    > span {
      color: @s-color;
    }
  }
}
</style>
